/*
 * ZooInspector
 * 
 * Copyright 2010 Colin Goodheart-Smithe

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
 */
package org.apache.zookeeper.inspector.manager;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;

import org.I0Itec.zkclient.ZkClient;
import org.I0Itec.zkclient.ZkConnection;
import org.I0Itec.zkclient.exception.ZkMarshallingError;
import org.I0Itec.zkclient.serialize.SerializableSerializer;
import org.I0Itec.zkclient.serialize.ZkSerializer;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.Watcher.Event.EventType;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.ZooDefs.Perms;
import org.apache.zookeeper.data.ACL;
import org.apache.zookeeper.data.Id;
import org.apache.zookeeper.data.Stat;
import org.apache.zookeeper.inspector.encryption.BasicDataEncryptionManager;
import org.apache.zookeeper.inspector.encryption.DataEncryptionManager;
import org.apache.zookeeper.inspector.logger.LoggerFactory;
import org.apache.zookeeper.server.auth.DigestAuthenticationProvider;

/**
 * @author CGSmithe
 * 
 */
public class ZooInspectorManagerImpl implements ZooInspectorManager {
	public static int systemFontSize = 16;

	private static final String A_VERSION = "ACL Version";
	private static final String C_TIME = "Creation Time";
	private static final String C_VERSION = "Children Version";
	private static final String CZXID = "Creation ID";
	private static final String DATA_LENGTH = "Data Length";
	private static final String EPHEMERAL_OWNER = "Ephemeral Owner";
	private static final String M_TIME = "Last Modified Time";
	private static final String MZXID = "Modified ID";
	private static final String NUM_CHILDREN = "Number of Children";
	private static final String PZXID = "Node ID";
	private static final String VERSION = "Data Version";
	private static final String ACL_PERMS = "Permissions";
	private static final String ACL_SCHEME = "Scheme";
	private static final String ACL_ID = "Id";
	private static final String SESSION_STATE = "Session State";
	private static final String SESSION_ID = "Session ID";
	private static final String FONT_SIZE = "Font Size";
	/**
	 * 
	 */
	public static final String CONNECT_STRING = "hosts";
	/**
	 * 
	 */
	public static final String USERNAME = "username";

	/**
	 * 
	 */
	public static final String PASSWORD = "password";
	/**
	 * 
	 */
	public static final String SESSION_TIMEOUT = "timeout";
	/**
	 * 
	 */
	public static final String DATA_ENCRYPTION_MANAGER = "encryptionManager";

	private static final File defaultsFile = new File("./config/defaultNodeVeiwers.cfg");

	private DataEncryptionManager encryptionManager;
	private String connectString;
	private int sessionTimeout;
	private ZkClient zkClient;
	private ZkConnection zkConnection;
	private final Map<String, NodeWatcher> watchers = new HashMap<String, NodeWatcher>();
	private List<ACL> acls = null;
	private Heartbeat heartbeat;
	
	public ZooInspectorManagerImpl() {
		heartbeat = new Heartbeat();
		heartbeat.start();
	}

	public boolean connect(Properties connectionProps) {
		try {
			if (this.zkClient == null) {
				String connectString = connectionProps.getProperty(CONNECT_STRING);
				String sessionTimeout = connectionProps.getProperty(SESSION_TIMEOUT);
				String encryptionManager = connectionProps.getProperty(DATA_ENCRYPTION_MANAGER);
				String username = connectionProps.getProperty(USERNAME);
				String password = connectionProps.getProperty(PASSWORD);
				String fontSize = connectionProps.getProperty(FONT_SIZE);
				if (fontSize != null && fontSize.matches("[\\d]+")) {
					systemFontSize = Integer.parseInt(fontSize.trim());
				}
				if (connectString == null || sessionTimeout == null) {
					throw new IllegalArgumentException("Both connect string and session timeout are required.");
				}
				if (encryptionManager == null) {
					this.encryptionManager = new BasicDataEncryptionManager();
				} else {
					Class<?> clazz = Class.forName(encryptionManager);

					if (Arrays.asList(clazz.getInterfaces()).contains(DataEncryptionManager.class)) {
						this.encryptionManager = (DataEncryptionManager) Class.forName(encryptionManager).newInstance();
					} else {
						throw new IllegalArgumentException(
								"Data encryption manager must implement DataEncryptionManager interface");
					}
				}
				this.connectString = connectString;
				this.sessionTimeout = Integer.valueOf(sessionTimeout);
				this.zkConnection = new ZkConnection(connectString, 300000);
				this.zkClient = new ZkClient(this.zkConnection, Integer.valueOf(sessionTimeout));
				if (username != null && !username.trim().isEmpty() && password != null && !password.trim().isEmpty()) {
					String key = username.trim() + ":" + password.trim();
					Id id = new Id("digest", DigestAuthenticationProvider.generateDigest(key));
					acls = new ArrayList<ACL>();
					acls.add(new ACL(ZooDefs.Perms.ALL, id));
					byte[] auth = key.getBytes();
					this.zkClient.addAuthInfo("digest", auth);
				}
				boolean exists = this.zkClient.exists("/");
				if(exists)
					heartbeat.setZkClient(this.zkClient);
				return exists;
			}
		} catch (Exception e) {
			e.printStackTrace();
			disconnect();
		}
		return false;
	}

	public boolean disconnect() {
		try {
			if (this.zkClient != null) {
				this.zkConnection.close();
				this.zkClient.close();
				this.zkClient = null;
			}
			heartbeat.setZkClient(null);
		} catch (Exception e) {
			this.zkClient = null;
			LoggerFactory.getLogger().error("Error occurred while disconnecting from ZooKeeper server", e);
		}
		if (this.zkClient == null) {
			if (acls != null) {
				acls.clear();
			}
			acls = null;
			return true;
		}
		return false;
	}

	public List<String> getChildren(String nodePath) throws KeeperException, InterruptedException {
		return this.zkClient.getChildren(nodePath);
	}

	class Heartbeat extends Thread {

		private ZkClient zkClient;

		public void setZkClient(ZkClient zkClient) {
			this.zkClient = zkClient;
		}

		public ZkClient getZkClient() {
			return zkClient;
		}

		@Override
		public void run() {
			System.out.println("心跳启动....");
			while (true) {
				try {
					if (zkClient != null) {
						System.out.println("心跳...");
						zkClient.readData("/");
					}
					Thread.sleep(60000);
				} catch (Exception e) {
				}

			}
		}

	}

	public String getData(String nodePath) {
		try {
			if (nodePath.length() == 0) {
				nodePath = "/";
			}
			if (this.zkClient.exists(nodePath)) {
				try {
					this.zkClient.setZkSerializer(new SerializableSerializer());
					return this.zkClient.readData(nodePath);
				} catch (Exception e) {
					this.zkClient.setZkSerializer(new ZkSerializer() {

						@Override
						public byte[] serialize(Object data) throws ZkMarshallingError {
							try {
								return ZooInspectorManagerImpl.this.encryptionManager.encryptData(data.toString());
							} catch (Exception e) {
								throw new ZkMarshallingError(e);
							}
						}

						@Override
						public Object deserialize(byte[] bytes) throws ZkMarshallingError {
							try {
								return ZooInspectorManagerImpl.this.encryptionManager.decryptData(bytes);
							} catch (Exception e) {
								throw new ZkMarshallingError(e);
							}
						}
					});
					return this.zkClient.readData(nodePath);
				}
				
			}
		} catch (Exception e) {
			LoggerFactory.getLogger().error("Error occurred getting data for node: " + nodePath, e);
		}
		return null;
	}

	public String getNodeChild(String nodePath, int childIndex) {
		try {
			if (this.zkClient.exists(nodePath)) {
				return this.zkClient.getChildren(nodePath).get(childIndex);
			}
		} catch (Exception e) {
			LoggerFactory.getLogger().error("Error occurred retrieving child " + childIndex + " of node: " + nodePath,
					e);
		}
		return null;
	}

	public int getNodeIndex(String nodePath) {
		int index = nodePath.lastIndexOf("/");
		if (index == -1 || (!nodePath.equals("/") && nodePath.charAt(nodePath.length() - 1) == '/')) {
			throw new IllegalArgumentException("Invalid node path: " + nodePath);
		}
		String parentPath = nodePath.substring(0, index);
		String child = nodePath.substring(index + 1);
		if (parentPath != null && parentPath.length() > 0) {
			List<String> children = null;
			try {
				children = this.getChildren(parentPath);
			} catch (KeeperException | InterruptedException e) {
				e.printStackTrace();
			}
			if (children != null) {
				return children.indexOf(child);
			}
		}
		return -1;
	}

	public List<Map<String, String>> getACLs(String nodePath) {
		List<Map<String, String>> returnACLs = new ArrayList<Map<String, String>>();
		Map<String, ACLInfo> map = new HashMap<String, ACLInfo>();
		try {
			if (nodePath.length() == 0) {
				nodePath = "/";
			}
			if (zkClient.exists(nodePath)) {
				Entry<List<ACL>, Stat> acl2 = zkClient.getAcl(nodePath);
				List<ACL> acls = acl2.getKey();
				for (ACL acl : acls) {
					String id = acl.getId().getId();
					ACLInfo aclInfo = map.get(id);
					if (aclInfo == null) {
						aclInfo = new ACLInfo();
					}
					aclInfo.setAclId(id);
					aclInfo.setAclScheme(acl.getId().getScheme());
					int perms = acl.getPerms();
					if ((perms & Perms.READ) == Perms.READ) {
						aclInfo.setRead(true);
					}
					if ((perms & Perms.WRITE) == Perms.WRITE) {
						aclInfo.setWrite(true);
					}
					if ((perms & Perms.CREATE) == Perms.CREATE) {
						aclInfo.setCreate(true);
					}
					if ((perms & Perms.DELETE) == Perms.DELETE) {
						aclInfo.setDelete(true);
					}
					if ((perms & Perms.ADMIN) == Perms.ADMIN) {
						aclInfo.setAdmin(true);
					}
					map.put(id, aclInfo);
				}
				for (Map.Entry<String, ACLInfo> entry : map.entrySet()) {
					ACLInfo aclInfo = entry.getValue();
					Map<String, String> aclMap = new LinkedHashMap<String, String>();
					aclMap.put(ACL_SCHEME, aclInfo.getAclScheme());
					aclMap.put(ACL_ID, aclInfo.getAclId());
					aclMap.put(ACL_PERMS, aclInfo.getAclPerms());
					returnACLs.add(aclMap);
				}

			}
		} catch (Exception e) {
			LoggerFactory.getLogger().error("Error occurred retrieving ACLs of node: " + nodePath, e);
		}
		return returnACLs;
	}

	public Map<String, String> getNodeMeta(String nodePath) {
		Map<String, String> nodeMeta = new LinkedHashMap<String, String>();
		try {
			if (nodePath.length() == 0) {
				nodePath = "/";
			}
			if (zkClient.exists(nodePath)) {
				Stat s = zkClient.getAcl(nodePath).getValue();
				nodeMeta.put(A_VERSION, String.valueOf(s.getAversion()));
				nodeMeta.put(C_TIME, String.valueOf(s.getCtime()));
				nodeMeta.put(C_VERSION, String.valueOf(s.getCversion()));
				nodeMeta.put(CZXID, String.valueOf(s.getCzxid()));
				nodeMeta.put(DATA_LENGTH, String.valueOf(s.getDataLength()));
				nodeMeta.put(EPHEMERAL_OWNER, String.valueOf(s.getEphemeralOwner()));
				nodeMeta.put(M_TIME, String.valueOf(s.getMtime()));
				nodeMeta.put(MZXID, String.valueOf(s.getMzxid()));
				nodeMeta.put(NUM_CHILDREN, String.valueOf(s.getNumChildren()));
				nodeMeta.put(PZXID, String.valueOf(s.getPzxid()));
				nodeMeta.put(VERSION, String.valueOf(s.getVersion()));
			}
		} catch (Exception e) {
			LoggerFactory.getLogger().error("Error occurred retrieving meta data for node: " + nodePath, e);
		}
		return nodeMeta;
	}

	public int getNumChildren(String nodePath) {
		try {
			if (this.zkClient.exists(nodePath)) {
				return this.zkClient.countChildren(nodePath);
			}
		} catch (Exception e) {
			LoggerFactory.getLogger().error("Error occurred getting the number of children of node: " + nodePath, e);
		}
		return -1;
	}

	public boolean hasChildren(String nodePath) {
		return getNumChildren(nodePath) > 0;
	}

	public void addAcl(List<String> paths, List<ACL> acls) throws KeeperException, InterruptedException {
		for (String path : paths) {
			List<ACL> acl = this.zkClient.getAcl(path).getKey();
			acl.addAll(acls);
			this.zkClient.setAcl(path, acl);
		}

	}

	@Override
	public void alterAcl(List<String> paths, List<ACL> acls) {
		if (acls == null) {
			ACL acl = new ACL(ZooDefs.Perms.ALL, new Id("world", "anyone"));
			List<ACL> list = new ArrayList<ACL>();
			list.add(acl);
			for (String path : paths) {
				this.zkClient.setAcl(path, list);
			}
			return;
		}
		for (String path : paths) {
			this.zkClient.setAcl(path, acls);
		}
	}

	public boolean isAllowsChildren(String nodePath) {
		try {
			if (this.zkClient.exists(nodePath)) {
				return this.zkClient.getAcl(nodePath).getValue().getEphemeralOwner() == 0;
			}
		} catch (Exception e) {
			LoggerFactory.getLogger().error("Error occurred determining whether node is allowed children: " + nodePath,
					e);
		}
		return false;
	}

	public Map<String, String> getSessionMeta() {
		Map<String, String> sessionMeta = new LinkedHashMap<String, String>();
		try {
			if (zkClient != null) {

				sessionMeta.put(SESSION_ID, String.valueOf(this.zkConnection.getZookeeper().getSessionId()));
				sessionMeta.put(SESSION_STATE, String.valueOf(this.zkConnection.getZookeeper().getState().toString()));
				sessionMeta.put(CONNECT_STRING, this.connectString);
				sessionMeta.put(SESSION_TIMEOUT, String.valueOf(this.sessionTimeout));
			}
		} catch (Exception e) {
			LoggerFactory.getLogger().error("Error occurred retrieving session meta data.", e);
		}
		return sessionMeta;
	}

	public boolean createNode(String parent, String nodeName) {
		try {
			String[] nodeElements = nodeName.split("/");
			for (String nodeElement : nodeElements) {
				String node = parent + "/" + nodeElement;
				if (!this.zkClient.exists(node)) {
					List<ACL> list = new ArrayList<ACL>();
					if (acls != null) {
						list.addAll(acls);
					}
					zkClient.createPersistent(node);
					parent = node;
				}
			}
			return true;
		} catch (Exception e) {
			LoggerFactory.getLogger().error("Error occurred creating node: " + parent + "/" + nodeName, e);
		}
		return false;
	}

	public boolean deleteNode(String nodePath) {
		try {
			if (this.zkClient.exists(nodePath)) {
				this.zkClient.deleteRecursive(nodePath);
			}
			return true;
		} catch (Exception e) {
			LoggerFactory.getLogger().error("Error occurred deleting node: " + nodePath, e);
		}
		return false;
	}

	public boolean setData(String nodePath, String data) {
		try {
			this.zkClient.writeData(nodePath, encryptionManager.decryptData(data.getBytes("UTF-8")));
			return true;
		} catch (Exception e) {
			LoggerFactory.getLogger().error("Error occurred setting data for node: " + nodePath, e);
		}
		return false;
	}

	public Pair<Map<String, List<String>>, Map<String, String>> getConnectionPropertiesTemplate() {
		Map<String, List<String>> template = new LinkedHashMap<String, List<String>>();
		Properties properties = new Properties();
		FileInputStream fileInputStream = null;
		try {
			fileInputStream = new FileInputStream(new File("./config/config.properties"));
			properties.load(fileInputStream);
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (fileInputStream != null) {
				try {
					fileInputStream.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
				fileInputStream = null;
			}
		}
		template.put(CONNECT_STRING,
				Arrays.asList(new String[] { properties.getProperty(CONNECT_STRING, "localhost:2181") }));
		template.put(USERNAME, Arrays.asList(""));
		template.put(PASSWORD, Arrays.asList(""));
		template.put(FONT_SIZE, Arrays.asList("16"));
		template.put(SESSION_TIMEOUT, Arrays.asList(new String[] { properties.getProperty(SESSION_TIMEOUT, "3000") }));
		template.put(DATA_ENCRYPTION_MANAGER,
				Arrays.asList(new String[] { properties.getProperty(DATA_ENCRYPTION_MANAGER,
						"org.apache.zookeeper." + "inspector.encryption.BasicDataEncryptionManager") }));
		Map<String, String> labels = new LinkedHashMap<String, String>();
		labels.put(CONNECT_STRING, "Zookeeper地址");
		labels.put(USERNAME, "用户名(可选)");
		labels.put(PASSWORD, "密码(可选)");
		labels.put(FONT_SIZE, "系统字体");
		labels.put(SESSION_TIMEOUT, "连接超时时间");
		labels.put(DATA_ENCRYPTION_MANAGER, "数据编码格式");
		return new Pair<Map<String, List<String>>, Map<String, String>>(template, labels);
	}

	public void addWatchers(List<String> selectedNodes, NodeListener nodeListener) {
		// add watcher for each node and add node to collection of
		// watched nodes
		for (String node : selectedNodes) {
			if (!watchers.containsKey(node)) {
				try {
					watchers.put(node, new NodeWatcher(node, nodeListener, zkClient));
				} catch (Exception e) {
					LoggerFactory.getLogger().error("Error occured adding node watcher for node: " + node, e);
				}
			}
		}
	}

	public void removeWatchers(List<String> selectedNodes) {
		// remove watcher for each node and remove node from
		// collection of watched nodes
		for (String node : selectedNodes) {
			if (watchers.containsKey(node)) {
				NodeWatcher watcher = watchers.remove(node);
				if (watcher != null) {
					watcher.stop();
				}
			}
		}
	}

	/**
	 * @author Colin
	 * 
	 */
	public class NodeWatcher implements Watcher {

		private final String nodePath;
		private final NodeListener nodeListener;
		private final ZkClient zkClient;
		private boolean closed = false;

		/**
		 * @param nodePath
		 * @param nodeListener
		 * @param zookeeper
		 * @throws InterruptedException
		 * @throws KeeperException
		 */
		public NodeWatcher(String nodePath, NodeListener nodeListener, ZkClient zkClient)
				throws KeeperException, InterruptedException {
			this.nodePath = nodePath;
			this.nodeListener = nodeListener;
			this.zkClient = zkClient;
			if (zkClient.exists(nodePath)) {
				zkClient.getChildren(nodePath);
			}
		}

		public void process(WatchedEvent event) {
			if (!closed) {
				try {
					if (event.getType() != EventType.NodeDeleted) {

						boolean bool = zkClient.exists(nodePath);
						if (bool) {
							zkClient.getChildren(nodePath);
						}
					}
				} catch (Exception e) {
					LoggerFactory.getLogger().error("Error occured re-adding node watcherfor node " + nodePath, e);
				}
				nodeListener.processEvent(event.getPath(), event.getType().name(), null);
			}
		}

		/**
		 * 
		 */
		public void stop() {
			this.closed = true;
		}

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorManager#
	 * loadNodeViewersFile(java.io.File)
	 */
	public List<String> loadNodeViewersFile(File selectedFile) throws IOException {
		List<String> result = new ArrayList<String>();
		if (defaultsFile.exists()) {
			FileReader reader = new FileReader(selectedFile);
			try {
				BufferedReader buff = new BufferedReader(reader);
				try {
					while (buff.ready()) {
						String line = buff.readLine();
						if (line != null && line.length() > 0) {
							result.add(line);
						}
					}
				} finally {
					buff.close();
				}
			} finally {
				reader.close();
			}
		}
		return result;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorManager#
	 * saveNodeViewersFile(java.io.File, java.util.List)
	 */
	public void saveNodeViewersFile(File selectedFile, List<String> nodeViewersClassNames) throws IOException {
		if (!selectedFile.exists()) {
			selectedFile.createNewFile();
		}
		FileWriter writer = new FileWriter(selectedFile);
		try {
			BufferedWriter buff = new BufferedWriter(writer);
			try {
				for (String nodeViewersClassName : nodeViewersClassNames) {
					buff.append(nodeViewersClassName);
					buff.append("\n");
				}
			} finally {
				buff.flush();
				buff.close();
			}
		} finally {
			writer.close();
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorManager#
	 * setDefaultNodeViewerConfiguration(java.io.File, java.util.List)
	 */
	public void setDefaultNodeViewerConfiguration(List<String> nodeViewersClassNames) throws IOException {
		File defaultDir = defaultsFile.getParentFile();
		if (!defaultDir.exists()) {
			defaultDir.mkdirs();
		}
		saveNodeViewersFile(defaultsFile, nodeViewersClassNames);
	}

	public List<String> getDefaultNodeViewerConfiguration() throws IOException {
		return loadNodeViewersFile(defaultsFile);
	}

	private void leadOut(String key, String value, PrintWriter printWriter) {
		printWriter.println(key + "=" + value);
	}

	private void leadOut(String path, PrintWriter printWriter, String parentPath, boolean isLeadOutParent) {
		List<String> children = zkClient.getChildren(path);
		if (children == null || children.isEmpty())
			return;
		for (String string : children) {
			if (string.trim().isEmpty())
				continue;
			String data = getData(path.equals("/") ? "" : path + "/" + string);
			data = data == null ? "" : data;
			String newPath = null;
			String readPath = null;
			if (path.equals("/")) {
				newPath = path + string;
				readPath = newPath;
			} else {
				readPath = path + "/" + string;
				if (parentPath.equals("/")) {
					newPath = readPath;
				} else {
					if (isLeadOutParent) {
						newPath = readPath;
					} else {
						newPath = readPath.substring(parentPath.length());
					}
				}
			}
			leadOut(newPath, data, printWriter);
			if (canRead(readPath)) {
				leadOut(readPath, printWriter, parentPath, isLeadOutParent);
			}
		}
	}

	private void getDatas(String path, Map<String, String> map, String parentPath) {
		List<String> children = zkClient.getChildren(path);
		if (children == null || children.isEmpty())
			return;
		for (String string : children) {
			if (string.trim().isEmpty())
				continue;
			String data = getData(path.equals("/") ? "" : path + "/" + string);
			data = data == null ? "" : data;
			String newPath = null;
			String readPath = null;
			if (path.equals("/")) {
				newPath = path + string;
				readPath = newPath;
			} else {
				readPath = path + "/" + string;
				if (parentPath.equals("/")) {
					newPath = readPath;
				} else {
					newPath = readPath.substring(parentPath.length());
				}
			}
			map.put(newPath, data);
			if (canRead(readPath)) {
				getDatas(readPath, map, parentPath);
			}
		}
	}

	public static String getFileNameOnSystem(String name) {
		return name.replaceAll("[:?\\*><\"\\|\\\\/]+", "__");

	}

	private boolean canRead(String path) {
		List<Map<String, String>> acLs2 = getACLs(path);
		String id = null;
		if (acls == null || acls.isEmpty()) {
			id = "anyone";
		} else {
			id = acls.get(0).getId().getId().trim();
		}
		boolean bool = false;
		for (Map<String, String> map : acLs2) {
			if (map.get(ACL_ID).equals(id) || map.get(ACL_ID).equals("anyone")) {
				String aclPerms = map.get(ACL_PERMS);
				String args[] = aclPerms.split(",");
				for (String string : args) {
					if (string.toLowerCase().trim().equals("read")) {
						bool = true;
						break;
					}
				}
			}
		}
		return bool;
	}

	@Override
	public boolean leadOut(List<String> paths, String savePath, boolean isLeadOutParent)
			throws UnsupportedEncodingException, FileNotFoundException {
		File file = new File(savePath);
		boolean bool = false;
		PrintWriter printWriter = null;
		try {
			printWriter = new PrintWriter(new OutputStreamWriter(new FileOutputStream(file), "UTF-8"), true);
			for (String parentPath : paths) {
				if (parentPath.length() <= 0) {
					parentPath = "/";
				}

				if (!parentPath.equals("/")) {
					if (!canRead(parentPath)) {
						continue;
					}
				}

				leadOut(parentPath, printWriter, parentPath, isLeadOutParent);
				bool = true;

			}
		} finally {
			if (printWriter != null)
				printWriter.close();
		}

		return bool;

	}

	@Override
	public void leadIn(List<String> selectedNodes, String path) throws IOException {
		if (path == null || path.trim().isEmpty()) {
			return;
		}
		InputStreamReader fileInputStream = null;
		try {
			fileInputStream = new InputStreamReader(new FileInputStream(path), "UTF-8");
			Properties properties = new Properties();
			properties.load(fileInputStream);
			for (String string : selectedNodes) {
				Set<Entry<Object, Object>> entrySet = properties.entrySet();
				for (Entry<Object, Object> entry : entrySet) {
					String newPath = string + entry.getKey();
					if (string.equals("/")) {
						newPath = newPath.substring(1);
					}
					if (acls != null && !acls.isEmpty()) {
						if (!zkClient.exists(newPath)) {
							zkClient.createPersistent(newPath, true, acls);
						}
					} else {
						if (!zkClient.exists(newPath)) {
							zkClient.createPersistent(newPath, true);
						}
					}
					Object data = entry.getValue();
					if (data == null || data.toString().trim().isEmpty()) {
						zkClient.writeData(newPath, "");
					} else {
						zkClient.writeData(newPath, data);
					}
				}
			}
		} finally {
			if (fileInputStream != null) {
				fileInputStream.close();
			}
		}

	}

	@Override
	public Map<String, String> getDatas(List<String> selectedNodes) {
		Map<String, String> map = new HashMap<String, String>();
		boolean bool = false;
		for (String parentPath : selectedNodes) {
			if (parentPath.length() <= 0) {
				parentPath = "/";
			}
			if (!parentPath.equals("/")) {
				if (!canRead(parentPath)) {
					continue;
				}
			}
			String pString = null;
			if (!parentPath.startsWith("/")) {
				pString = "/" + parentPath;
			} else {
				pString = parentPath;
			}
			pString = getFileNameOnSystem(pString + ".properties");
			getDatas(parentPath, map, parentPath);
			bool = true;

		}
		if (!bool)
			return null;
		return map;
	}

	@Override
	public void writeDatas(List<String> selectedNodes, Map<String, String> map) {
		for (String string : selectedNodes) {
			for (Map.Entry<String, String> entry : map.entrySet()) {
				String newPath = string + entry.getKey();
				if (string.equals("/")) {
					newPath = newPath.substring(1);
				}
				if (acls != null && !acls.isEmpty()) {
					if (!zkClient.exists(newPath)) {
						zkClient.createPersistent(newPath, true, acls);
					}
				} else {
					if (!zkClient.exists(newPath)) {
						zkClient.createPersistent(newPath, true);
					}
				}
				Object data = entry.getValue();
				if (data == null || data.toString().trim().isEmpty()) {
					zkClient.writeData(newPath, "");
				} else {
					zkClient.writeData(newPath, data);
				}
			}
		}

	}

	@Override
	public void deleteDatas(List<String> selects, Map<String, String> map) {
		for (String string : selects) {
			Set<Entry<String, String>> entrySet = map.entrySet();
			List<Entry<String, String>> list = new ArrayList<Entry<String, String>>();
			list.addAll(entrySet);
			Collections.sort(list, new Comparator<Entry<String, String>>() {

				@Override
				public int compare(Entry<String, String> o1, Entry<String, String> o2) {
					return -(o1.getKey().split("/").length - o2.getKey().split("/").length);
				}
			});
			for (Entry<String, String> entry : list) {
				String newPath = string + entry.getKey();
				if (string.equals("/")) {
					newPath = newPath.substring(1);
				}
				if (acls != null && !acls.isEmpty()) {
					if (zkClient.exists(newPath)) {
						zkClient.delete(newPath);
					}
				} else {
					if (zkClient.exists(newPath)) {
						zkClient.delete(newPath);
					}
				}
			}
		}

	}
}
